#ifndef XGE_XWORKER_QUEUE_H_
#define XGE_XWORKER_QUEUE_H_
#include "xge_common_config.h"
#include "xtask.h"
#include "xdata_structure.h"
#include "xbase_common.h"
#include "xatomic_operation.h"
#include "xallocator.h"

//$Revision: 2011/11/23
// 1. Delete the virtual function "Terminate" 
// I have thought many times that is a terminate function really useful ?
// No , I totally forbid the calling of this function in the whole engine from now on
// since it can easily lead to dead-lock , definitely , the XThreadHandlePtr will not
// support Terminate function as well.
//$



namespace xge{
namespace xge_internal{

  // This worker_queue is the basic type of worker queue
  // it simply provide a virtual interface for the specific
  // worker queue instead of implementing only one worker queue

  class WorkerPriorityQueue;
  class WorkerSingleQueue;
  class WorkerDoubleQueue;

#define CHECK_RANGE(s,Down,Up) XASSERT((s)>=(Down) && (s)<=(Up))

  class WorkerQueue : public Allocator {
  public:
    // Push a task into this queue , this task queue will never acquire the worker type 
    // since yourself may know the downcast
    // Since different worker queue may have different type of worker queue data structure
    // so I do not acquire this worker queue to use a common type of data structure 
    virtual bool PushTask( const TaskPtr& task_ptr ) = 0;
    // Using list to do this task pushing container :
    // In fact , our queue has definitely two type of internal structure
    // list and vector
    // vector you have to copy every one ( or swap if you can do so )
    // but list can helps a lot by using split operation or even swap 
    // so choosing list as our collection is suitable
    typedef xge::list< TaskPtr >::List TaskPtrList;     
    // Push multiple task into the queue .
    // This is pretty useful to avoid the locking stuff when dealing with the multi-thread
    // environment .
    // The original task_ptr_list will be changed during this operation !
    // return the size of pushed task .
    virtual size_t PushTask( TaskPtrList* task_ptr_list ) =0;
    // Try push a task is another version of push task stuff
    // it just try to push a task into the internal data buffer , since the specific implementation
    // of worker queue maybe a lockable queue , so the PushTask may wait for the certain singal happen
    // so try to push task provide a way to try . This is pretty useful when dealing with this kind of
    // push work :
    // you try to push a task , failed , wait to next loop , try again , wait to next loop , this
    // can lead good performance and good flexibility 
    virtual bool TryPushTask( const TaskPtr& task_ptr ) =0;
    // WaitWhenRelease this worker queue until it finished 
    // When the worker queue is empty or empty again 
    // the  WaitWhenRelease will return , otherwise it will wait
    // until it return
    // WaitWhenRelease only return when the target queue status is RELEASE
    virtual bool WaitWhenRelease() = 0;
    // WaitWhenRelease a specific time elapse , this function can not
    // be that precise , if the Whole System Timer won't increase the
    // precision , this timer will stay as the average standard that
    // not below 15.9ms 
    // that means WaitWhenRelease(1) will return at least after 15.9 ms
    // WaitWhenRelease only return when the target queue status is RELEASE
    virtual const TripleResult WaitWhenRelease( int ms ) =0;
    // Wait until empty
    // This is different version , it only return when the LOCKED QUEUE ( Single queue is that queue , double queue is the back queue )
    // is empty .
    virtual bool WaitWhenEmpty() =0;
    virtual const TripleResult WaitWhenEmpty( int ms ) = 0;
  public:
    // The worker queue will generally divided into three type
    // 1. Single Queue // This can delete the task
    // 2. Double Queue // This can just run without delete provided
    // 3. Single Priority Queue // This can delete the task and also can execute the task as priority order
    // 4. User Defined Queue // This has no specification , you just define this queue , and the user defined task will push into this queue
    enum {
      QUEUE_TYPE_SINGLE_QUEUE = 1,
      QUEUE_TYPE_DOUBLE_QUEUE = 2,
      QUEUE_TYPE_PRIROITY_QUEUE=3,
      QUEUE_TYPE_USER_QUEUE = 4
    };

    int queue_type() const {
      return queue_type_;
    }

  private:
    template < typename WorkerQueue > struct QueueType {
      static const size_t Value = QUEUE_TYPE_USER_QUEUE;
    };
    template <> struct QueueType< WorkerPriorityQueue > {
      static const size_t Value = QUEUE_TYPE_PRIROITY_QUEUE;
    };
    template <> struct QueueType< WorkerSingleQueue > {
      static const size_t Value = QUEUE_TYPE_SINGLE_QUEUE;
    };
    template <> struct QueueType< WorkerDoubleQueue > {
      static const size_t Value = QUEUE_TYPE_DOUBLE_QUEUE;
    };
  public:
    // Down cast implementation
    // Try to play it safe , however , you make the decision 
    template < class ReturnQueue > ReturnQueue* DownCast() const{
      XASSERT(this->queue_type() == QueueType<ReturnQueue>::Value );
      return (ReturnQueue*)(this);
    }

  public:
    // queue status 
    enum {
      STATUS_PREPARE=0 , // This worker queue is preparing , which means it has not bind to a specific worker thread 
                         // because worker thread can have many worker queue , so it may delayed 
      STATUS_SLEEP = 1 , // This worker queue is sleeping now
      STATUS_WORKING=2 , // This worker queue is working now
      STATUS_LOCKING=3 , // This is always happen when the target working thread is about to quit
                         // when the main thread has post the about to quit message , the worker queue will be locked 
                         // and the locked queue will not be able to push task , any push task operation will fail at once
      STATUS_RELEASE=4   // This worker queue has exit
    };

    int status() const {
      return status_;
    }
    // get the status of this queue with atomic operation
    // this is pretty useful when the sub worker queue is
    // the ThreadWorker type
    int status_atomic() const {
      return (int)LoadAndAcquire(status_);
    }

    // last loop return value
    // int ret = Loop();
    // ret is the same as last_loop_return
    int last_loop_return() const {
      return last_loop_return_;
    }
    // Binding type is Remote or Local
    // if you put this queue in the main thread ,
    // the binding type is Local
    // otherwise it is Remote
    // using REMOTE_BINDING and LOCAL_BINDING
    int binding_type() const {
      return binding_type_;
    }
    // testing if this class is sleep or not
    bool sleep() const {
      return status() == STATUS_SLEEP;
    }



    virtual ~WorkerQueue() ;

    WorkerQueue( int queue_type, int binding_type ) : status_( STATUS_PREPARE ) , queue_type_(queue_type) {
      CHECK_RANGE(queue_type_,QUEUE_TYPE_SINGLE_QUEUE,QUEUE_TYPE_USER_QUEUE);
      CHECK_RANGE(binding_type,REMOTE_BINDING,LOCAL_BINDING);
      binding_type_ = binding_type;
    }

  public:
    // Post quit to this message loop
    // This quit is not forced to
    // it just a recommend , the worker queue
    // can definitely ignore this indicator
    // and running as you wish
    // however the upper caller may force this termination
    virtual void PostQuitSoon() = 0;
    // Post quit when the Locking Queue( Double Queue Worker means the back queue ) is empty
    // This may be little slow than the PostQuitSoon ;
    virtual void PostQuitWhenEmpty() = 0;
    // loop return value
    enum {
      LOOP_OK_RETURN = 0 , // This means the worker queue has successfully exit 
      LOOP_FAILED_RETURN=1 // This means the worker queue has failed 
    };
    // Loop function is the main routine inside this worker queue
    // the shell thread will call this function in its thread main delegate
    // in this loop , the worker queue may watch out for the input new task
    // may simply wait , or work .
    // the status code is very important for the upper worker thread
    virtual int Loop() = 0;

  protected:

    // set the status of this queue
    void set_status( int s ) {
      CHECK_RANGE(s,STATUS_PREPARE,STATUS_RELEASE);
      status_ = s;
    }
    // set the status of this queue with atomic operation
    // this is pretty useful when the sub worker queue is
    // the ThreadWorker type
    void set_status_atomic( int s ) {
      StoreAndRelease(status_,s);
    }
    void set_last_loop_return( int r ) {
      CHECK_RANGE(r,LOOP_OK_RETURN,LOOP_FAILED_RETURN);
      last_loop_return_ = r;
    }

    void set_binding_type( int t ) {
      CHECK_RANGE(t,REMOTE_BINDING,LOCAL_BINDING);
      binding_type_ = t;
    }

  private:
    // status code
    volatile int status_;
    // last loop return code
    int last_loop_return_;
    // binding type code
    int binding_type_;
    // queue type code
    const int queue_type_;
  };

#undef CHECK_RANGE


  typedef SharedPtr< WorkerQueue , DefaultDestructor<WorkerQueue> > WorkerQueuePtr;

}// namespace xge_internal
}// namespace xge

#endif// XGE_XWORKER_QUEUE_H_